/*
定长类型( sized  )，这些类型的大小在编译时是已知的
不定长类型( unsized DST)，与定长类型相反，它的大小只有到了程序运行时才能动态获知，这种类型又被称之为 DST
*/
// 栈 指针  固定大小:   集合的内存地址、元素数目、分配空间信息
// 堆 --> 数据:实际数据
// 正因为编译器无法在编译期获知类型大小，若你试图在代码中直接使用 DST 类型，将无法通过编译

/*
DST 类型有: str、[T]、dyn Trait，它们都无法单独被使用，必须要通过引用或者 Box 来间接使用 。
   str( String 和 &str 的底层数据类型)
sized  &str(它的引用存储在栈上)
*/

/*将动态数据固定化的秘诀就是  使用引用指向这些动态数据，然后在引用中存储相关的内存位置、长度等信息*/

#[cfg(test)]
mod dst_test {
    // 常量泛型
    // 常量泛型参数允许程序项在常量值上泛型化。const标识符为常量参数引入了一个名称，并且该程序项的所有实例必须用给定类型的值去实例化该参数。
    fn my_function<const N: usize>(n: usize) {
        let array = [123; N]; //泛型约束
        // 编译器无法在编译期得知该类型值的大小，只有到了程序运行时，才能动态获知
        // let array = [123; n]; // n不确定
    }

    #[test]
    fn str() {
        // str 既不是 String 动态字符串，也不是 &str 字符串切片，而是一个 str
        // 是一个动态类型，同时还是 String 和 &str 的底层数据类型
        // error
        // let s1: str = "Hello there!";
        // let s2: str = "How's it going?";

        // ok
        let s3: &str = "on?";
    }

    // 特征对象
    trait MyThing {}

    fn foobar_1(thing: &dyn MyThing) {}

    // OK
    fn foobar_2(thing: Box<dyn MyThing>) {} // OK
    // fn foobar_3(thing: MyThing) {}          // ERROR!
}

// Sized 特征:所有在编译时就能知道其大小的类型，都会自动实现 Sized 特征
// 每一个特征都是一个可以通过名称来引用的动态大小类型
#[cfg(test)]
mod sized_test {
    fn generic<T: Sized>(t: T) {
        // --snip--
    }

    // 想在泛型函数中使用动态数据类型
    fn generic2<T: ?Sized>(t: &T) {
        // 函数参数类型从 T 变成了 &T，因为 T 可能是动态大小的，因此需要用一个固定大小的指针(引用)来包裹它。
        // --snip--
    }
}

#[cfg(test)]
mod box_test {
    #[test]
    fn str() {
        // let s1: Box<str> = Box::new("Hello there!" as str); //err
        // let s1: Box<str> = "Hello there!".into();
        let s1: Box<&str> = "Hello there!".into();
    }
}
